1 module hunt.templates.renderer; 2 3 import std.string; 4 import std.file; 5 import std.path; 6 import std.conv; 7 import std.variant; 8 import std.json; 9 import std.stdio; 10 import std.uni; 11 import std.functional; 12 13 import hunt.templates.rule; 14 import hunt.templates.element; 15 import hunt.templates.match; 16 import hunt.templates.ast; 17 import hunt.templates.util; 18 19 class Renderer 20 { 21 public: 22 this() 23 { 24 } 25 26 bool execCmp(T)(T a, T b, Function func) 27 { 28 //writeln("--------exec cmp : ", func); 29 switch (func) 30 { 31 case Function.Equal: 32 { 33 return binaryFun!("a == b")(a, b); 34 } 35 case Function.Greater: 36 { 37 return binaryFun!("a > b")(a, b); 38 } 39 case Function.Less: 40 { 41 return binaryFun!("a < b")(a, b); 42 } 43 case Function.GreaterEqual: 44 { 45 return binaryFun!("a >= b")(a, b); 46 } 47 case Function.LessEqual: 48 { 49 return binaryFun!("a <= b")(a, b); 50 } 51 case Function.Different: 52 { 53 return binaryFun!("a != b")(a, b); 54 } 55 default: 56 { 57 return false; 58 } 59 } 60 } 61 62 bool cmp(JSONValue a, JSONValue b, Function func) 63 { 64 //writeln("--------cmp : ", func, " a type :", a.type); 65 if (a.type != b.type) 66 return false; 67 if (a.type == JSONType.object || a.type == JSONType.array) 68 return false; 69 else if (a.type == JSONType..string) 70 { 71 return execCmp!string(a.str, b.str, func); 72 } 73 else if (a.type == JSONType.integer) 74 { 75 return execCmp!long(a.integer, b.integer, func); 76 } 77 else if (a.type == JSONType.true_) 78 { 79 return true; 80 } 81 else if (a.type == JSONType.false_) 82 { 83 return true; 84 } 85 else 86 return false; 87 } 88 89 bool eval(JSONValue j) 90 { 91 if (j.type == JSONType.object || j.type == JSONType.array) 92 return true; 93 else if (j.type == JSONType..string) 94 { 95 return j.str.length > 0 ? true : false; 96 } 97 else if (j.type == JSONType.integer) 98 { 99 return j.integer > 0 ? true : false; 100 } 101 else if (j.type == JSONType.true_) 102 { 103 return true; 104 } 105 else if (j.type == JSONType.false_) 106 { 107 return false; 108 } 109 else 110 return true; 111 } 112 113 T eval_expression(T = JSONValue)(ElementExpression element, ref JSONValue data) 114 { 115 auto var = eval_function!(T)(element, data); 116 return var; 117 } 118 119 T eval_function(T = JSONValue)(ElementExpression element, ref JSONValue data) 120 { 121 T result; 122 //writeln("------element.func---- :", element.func); 123 switch (element.func) 124 { 125 126 case Function.Upper: 127 { 128 auto res = eval_expression(element.args[0], data); 129 if (res.type == JSONType..string) 130 result = toUpper(res.str); 131 else 132 result = toUpper(res.toString); 133 return result; 134 } 135 case Function.Lower: 136 { 137 auto res = eval_expression(element.args[0], data); 138 if (res.type == JSONType..string) 139 result = toLower(res.str); 140 else 141 result = toLower(res.toString); 142 return result; 143 } 144 case Function.Equal: 145 case Function.Greater: 146 case Function.Less: 147 case Function.GreaterEqual: 148 case Function.LessEqual: 149 case Function.Different: 150 { 151 result = cmp(eval_expression(element.args[0], data), 152 eval_expression(element.args[1], data), element.func); 153 return result; 154 } 155 case Function.ReadJson: 156 { 157 try 158 { 159 //writeln("--read * json --:", element.command); 160 if (element.command.length > 0 && element.command in data) 161 result = data[element.command]; 162 else 163 { 164 auto cmds = split(element.command, "."); 165 if (cmds.length > 1) 166 { 167 if (cmds.length == 2) 168 { 169 if (cmds[0] in data) 170 { 171 if (Util.is_num(cmds[1])) 172 { 173 auto idx = to!int(cmds[1]); 174 175 result = data[cmds[0]][idx]; 176 } 177 else if (cmds[1] in data[cmds[0]]) 178 result = data[cmds[0]][cmds[1]]; 179 } 180 181 } 182 } 183 else 184 result = element.command; 185 } 186 } 187 catch (Exception e) 188 { 189 template_engine_throw("render_error", 190 "variable '" ~ element.command ~ "' not found"); 191 } 192 break; 193 } 194 case Function.Result: 195 { 196 //writeln("--read result --:", element.result.toString); 197 result = element.result; 198 return result; 199 } 200 case Function.Default: 201 { 202 //writeln("-----Function.Default----"); 203 try 204 { 205 return eval_expression!(T)(element.args[0], data); 206 } 207 catch (Exception e) 208 { 209 return eval_expression!(T)(element.args[1], data); 210 } 211 } 212 default: 213 { 214 template_engine_throw("render_error", 215 "function '" ~ to!string(element.func) ~ "' not found"); 216 } 217 } 218 219 template_engine_throw("render_error", "unknown function in renderer: " ~ element.command); 220 return T(); 221 } 222 223 string render(ASTNode temp, ref JSONValue data) 224 { 225 string result = ""; 226 //writeln("------temp.parsed_node.children-----: ",temp.parsed_node.children.length); 227 foreach (element; temp.parsed_node.children) 228 { 229 //writeln("------element.type-----: ",element.type); 230 switch (element.type) 231 { 232 case Type.String: 233 { 234 auto element_string = cast(ElementString)(element); 235 result ~= element_string.text; 236 break; 237 } 238 case Type.Expression: 239 { 240 auto element_expression = cast(ElementExpression)(element); 241 auto variable = eval_expression(element_expression, data); 242 243 // writeln("-----variable.type-------: ",variable.type); 244 if (variable.type == JSONType..string) 245 { 246 result ~= variable.str; 247 } 248 else 249 { 250 251 result ~= variable.toString; 252 } 253 break; 254 } 255 case Type.Loop: 256 { 257 auto element_loop = cast(ElementLoop)(element); 258 switch (element_loop.loop) 259 { 260 case Loop.ForListIn: 261 { 262 auto list = eval_expression(element_loop.list, data); 263 //writeln("----list ----: ", list); 264 if (list.type != JSONType.array) 265 { 266 template_engine_throw("render_error", 267 list.toString ~ " is not an array"); 268 } 269 foreach (size_t k, v; list) 270 { 271 //writeln("v.type : ",v.type, " v.tostring :",v.toString); 272 JSONValue data_loop = parseJSON(data.toString); 273 data_loop["index"] = k; 274 data_loop[element_loop.value] = v; 275 result ~= render(new ASTNode(element_loop), data_loop); 276 } 277 break; 278 } 279 case Loop.ForMapIn: 280 { 281 auto map = eval_expression(element_loop.list, data); 282 //writeln("----Loop type ----: ", map.type," map.toString : ",map.toString); 283 if (map.type != JSONType.object) 284 { 285 template_engine_throw("render_error", 286 map.toString ~ " is not an object"); 287 } 288 foreach (string k, v; map) 289 { 290 JSONValue data_loop = data; 291 data_loop[element_loop.key] = k; 292 data_loop[element_loop.value] = v; 293 result ~= render(new ASTNode(element_loop), data_loop); 294 } 295 break; 296 } 297 default: 298 { 299 break; 300 } 301 } 302 303 break; 304 } 305 case Type.Condition: 306 { 307 auto element_condition = cast(ElementConditionContainer)(element); 308 foreach (branch; element_condition.children) 309 { 310 auto element_branch = cast(ElementConditionBranch)(branch); 311 //writeln("-----element_branch.type-------: ",element_branch.condition_type); 312 auto flg = eval_expression(element_branch.condition, data); 313 //writeln("-----flg.type-------: ",flg.type); 314 if (element_branch.condition_type == Condition.Else || eval(flg)) 315 { 316 result ~= render(new ASTNode(element_branch), data); 317 break; 318 } 319 } 320 break; 321 } 322 default: 323 { 324 break; 325 } 326 } 327 } 328 return result; 329 } 330 }